<?php


namespace App\Repositories\Lottery;


use App\Exceptions\BasicException;
use App\Models\MainDB\Lottery\CompensatePool;
use App\Models\MainDB\Lottery\Draw;
use App\Models\MainDB\Lottery\DrawRecord;
use App\Models\MainDB\Lottery\Plate;
use App\Models\MainDB\Lottery\Pool;
use App\Models\MainDB\Lottery\User;
use App\Models\MainDB\GiftBackpack;
use App\Models\MainDB\Lottery\UserStatDate;
use App\Services\Tools\PushChatService;
use App\Services\Tools\PushService;
use App\Services\Tools\RedisService;
use App\Traits\Singleton;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;

class LotteryRepository
{
    use Singleton;

    /**
     * $setting
     * 获取补偿金币参数
     * novice_count 新手抽奖次数 config.novice_limit_frequency = 500
     * novice_prize_max_coin 新手最大礼物单价 config.novice_max_gift = 60000
     * novice_compensate_min_coin 新手补偿最小金币 config.novice_makeup_spot = 6000
     * novice_compensate_max_coin 新手补偿最大金币 config.novice_makeup_spot + 购买余额单价 * 111 = 28200
     * novice_min_input_output_rate 新手最小投入产出比 config.novice_minrate = 0.8
     * min_input_output_rate 最小投入产出比 config.minrate = 0.75
     * output_rate 总体产出比 config.output_ratio = 1
     * lucky_difference 幸运差 C(LUCKDRAW_DIFFERENCE)
     * lucky_prize_limit 大奖判定 config.large_gift_limit = 30000
     * lucky_user_min_count 获取大礼物用户最小抽奖次数. 原系统无配置.写死的值 = 1500
     */

    // 是否补偿
    public function isCompensate($user, $setting, $gameStat)
    {
        $isCompensate   = false;                                                          // 是否需要补偿
        $userOutputRate = $user->input == 0 ? 0 : round($user->output / $user->input, 2); // 用户产出投入比
        $profit         = $user->output - $user->input;                                   // 用户盈利
        if ($user->novice) { // 新手
            // 用户投入 >= 系统配置(新手补偿最小金币) || 用户投入 <= 系统配置(新手补偿最大金币)
            $inputCondition = $user->input >= $setting->novice_compensate_min_coin || $user->input <= $setting->novice_compensate_max_coin;
            // 用户投入产出比 < 系统设置(新手最小产出比)  && 系统产出 > 系统设置(新设补偿最小金币)
            $outputCondition = $userOutputRate < $setting->novice_min_input_output_rate && $gameStat->output > $setting->novice_compensate_min_coin;
            if ($inputCondition && $outputCondition) {
                $isCompensate = true;
            }
        } else { // 非新手
            // 用户投入产出比 < 系统设置(最小投入产出比)
            // if ($userOutputRate < $setting->min_input_output_rate) {
            //     $isCompensate = true;
            // }

            // 用户金额投入大于等于12000（人民币）时，看用户亏损额，亏损额超过3000才开始回血
            if ($user->input >= 120000) {
                if ($profit < 0 && abs($profit) > 300000) {
                    $isCompensate = true;
                }
            } else { // 用户金额投入小于12000（人民币）时，看用户亏损比例，投入产出比小于75%时，才开始回血
                if ($userOutputRate < $setting->min_input_output_rate) {
                    $isCompensate = true;
                }
            }
        }
        // // 需要补偿 && 用户亏损 && ABS(亏损金额) > 用户需要补偿的金额
        $compensateLossCoin = $setting->compensate_loss_coin <= 0 ? 300000 : $setting->compensate_loss_coin;
        if ($isCompensate && $profit < 0 && abs($profit) > $compensateLossCoin) {
            $isCompensate = true;
        }

        return $isCompensate;
    }

    // 是否幸运, 出大礼物
    public function isLucky($user, $setting, $gameStat)
    {
        if ($user->novice) {
            return false;
        }
        $systemOutputRate = $gameStat->input == 0 ? 0 : round($gameStat->output / $gameStat->input, 2);
        if ($systemOutputRate >= $setting->output_rate - $setting->lucky_difference) {
            return false;
        }
        $systemProfit = $gameStat->output - $gameStat->input;
        if ($systemProfit < 0 && abs($systemProfit) < 10000) {
            return false;
        }
        return true;
    }

    // 是否新手
    public function isNovice(int $userCount, int $noviceCount)
    {
        if ($userCount < $noviceCount) {
            return true;
        }
        return false;
    }

    // 获取用户奖品最大金额
    public function getUserPrizesMaxCoin($user, $setting, $gameStat, $isCompensate)
    {
        if ($user->count < $setting->novice_count) {
            return $setting->novice_prize_max_coin;
        }
        $isLucky      = $this->isLucky($user, $setting, $gameStat);
        $gameProfit   = $gameStat->input - $gameStat->output;
        $totalOPRatio = $gameStat->input == 0 ? 0 : round($gameStat->output / $gameStat->input, 2);

        $gameCondition            = $gameProfit < 0 && $setting->lucky_prize_limit <= abs($gameProfit);
        $userCountCondition       = $user->count < $setting->lucky_user_min_count;
        $gameOutputRatioCondition = $totalOPRatio > 1 && !$isCompensate;

        if (!$isLucky && $gameCondition && $userCountCondition && $gameOutputRatioCondition) {
            return $setting->lucky_prize_limit;
        }

        $coin = $user->input - $user->output; // 原算法 = 产出 - 投入 * 系统配置(产出比)
        return $coin <= 100 ? 10000 : $coin;
        // return $gameStat->input - $gameStat->output * $setting->output_ratio; // 原算法 = 产出 - 投入 * 系统配置(产出比)
    }

    // 获取补偿
    public function getCompensate(array $allPrizes, int $compensateCoin, int $minCompensateCoin = 100)
    {
        if (empty($allPrizes) || $compensateCoin <= 0) {
            return [];
        }
        $allPrizes = Collection::make($allPrizes);
        $result    = [];
        if ($compensateCoin > $minCompensateCoin) {
            if ($compensateCoin == 600000) {
                //     $compensatePrize = $allPrizes->sortByDesc('price')->where('price', '>=', '131400')
                //         ->where('price', '<', $compensateCoin)->random();
                $compensatePrize = CompensatePoolRepository::getInstance()->drawSingle();
            } elseif ($compensateCoin == 334400) { // 区间随机
                $compensatePrize = $allPrizes->sortByDesc('price')
                    ->where('price', '>', '100000')
                    ->where('price', '<=', $compensateCoin)->random();
            } else {
                $compensatePrize = $allPrizes->sortByDesc('price')->where('price', '<', $compensateCoin)->first();
            }
            // $compensatePrize = $allPrizes->sortByDesc('price')->where('price', '<', $compensateCoin)->first();

        }
        if (!isset($compensatePrize)) { // 不存在取最小奖品
            $compensatePrize = $allPrizes->sortBy('price')->first();
        }

        if ($compensatePrize) {
            $result = [$compensatePrize['prize_id'] => 1];
        }
        return $result;
    }

    //开始抽奖
    public function lottery($prizes, $count)
    {
        $result = [];
        if ($count <= 0 || empty($prizes)) {
            return $result;
        }
        $totalWeight = max(array_column($prizes, 'end'));
        for ($i = 0; $i < $count; $i++) {
            $randomNum = mt_rand(0, $totalWeight - 1);
            foreach ($prizes as $item) {
                if ($randomNum >= $item['start'] && $randomNum <= $item['end']) {
                    if (isset($result[$item["prize_id"]])) {
                        $result[$item["prize_id"]] += 1;
                    } else {
                        $result[$item["prize_id"]] = 1;
                    }
                    break;
                }
            }
        }
        return $result;
    }

    //普通爆率
    public function drawPrizes($jackpotsList, $isWeight = true)
    {
        $currentWeight = 0;
        foreach ($jackpotsList as $key => $value) {
            if ($value['weight'] <= 0) {
                continue;
            }
            $jackpotsList[$key]['start'] = $currentWeight;
            $currentWeight               += $isWeight == 1 ? $value['rate'] + $value['weight'] : $value['rate'];
            $jackpotsList[$key]['end']   = $currentWeight;
            $currentWeight               += 1;
        }
        return $jackpotsList;
    }

    public function draw(int $userId, int $count, $plateId, $poolType = 'default')
    {
        DB::beginTransaction();
        try {
            $drawCount = $count;
            $plate     = Plate::query()->where('id', $plateId)->first();
            if (!$plate) {
                throw new BasicException(-1, '非法请求');
            }
            $setting = SettingRepository::getInstance()->one([], true); // 游戏配置
            if (!$setting || !$setting->is_open) {
                throw new BasicException(-1, '游戏未开启');
            }
            $gameStat   = PoolRepository::getInstance()->getTotalInputOutput(); // 游戏统计
            $drawOutput = 0;                                                    // 抽奖产出
            $drawInput  = $count * $setting->balance_coin;                      // 抽奖投入
            $user       = UserRepository::getInstance()->find($userId, false, true);
            if (!$user || $user->balance < $count) { // 验证用户余额
                throw new BasicException(-1, '物资不足，请购买勋章获得物资');
            }
            $user->novice = $this->isNovice($user->count, $setting->novice_count);
            $isCompensate = $this->isCompensate($user, $setting, $gameStat);                        // 是否补偿
            $prizeMaxCoin = $this->getUserPrizesMaxCoin($user, $setting, $gameStat, $isCompensate); // 奖品最大金额
            $allPrizes    = PrizeRepository::getInstance()->all()->transform(function ($item) {
                $item->price = $item->gift->price ?? 0;
                return $item;
            }); // 全部奖品
            $poolPrizes   = $allPrizes->filter(function ($item) use ($prizeMaxCoin) {
                return $item->gift->price < $prizeMaxCoin;
            })->transform(function ($item) {
                return $item->only(['prize_id', 'weight', 'rate']);
            });
            $poolPrizes   = $this->drawPrizes($poolPrizes->toArray(), $setting->is_weight); // 修改奖池奖品数据结构
            // ================================================ 抽奖 ================================================
            $compensateCoin   = $user->compensate_coin;
            $compensatePrizes = [];
            $systemCompensate = false;
            if (($gameStat->input - $gameStat->output) > 200000 && $user->input > 100000) { //
                $isCompensate     = true;
                $systemCompensate = true;
            }
            if ($count <= 1) { // 单抽不进行补偿
                $isCompensate = false;
            }

            if ($isCompensate) { // 补偿
                $isSanction = $user->is_sanction ?? 0;
                $recoupCoin = $user->input - $user->output <= 100 ? 100 : $user->input - $user->output;
                // if( ($gameStat->input - $gameStat->output) > 200000 && $user->input > 500000){
                //     $recoupCoin = 600000;
                // }
                // 非新手: 用户金额投入大于等于12000（人民币）时，看用户亏损额，亏损额超过3000
                $userDateInput = $user->date->input ?? 0;
                if (!$user->novice && $user->input >= 120000) { //
                    $recoupCoin = 334400;
                }
                if ($isSanction == 0 && $systemCompensate && $userDateInput >= 20000) { // 平台补偿固定价值
                    $recoupCoin = 600000;
                }
                if ($compensateCoin > 0 && $user->output - $user->input < 0 && abs($user->output - $user->input) > $compensateCoin) {
                    $compensatePrizes = $this->getCompensate($allPrizes->toArray(), $compensateCoin);
                } elseif ($count < 10) {
                    $compensatePrizes = $this->getCompensate($allPrizes->toArray(), $recoupCoin);
                } else {
                    $compensatePrizes = $this->getCompensate($allPrizes->toArray(), $recoupCoin);
                }
                if (!empty($compensatePrizes)) {
                    $drawCount -= 1;
                }
            }
            $drawPrizes = $this->lottery($poolPrizes, $drawCount); // 抽奖
            // 追加补偿
            foreach ($compensatePrizes as $prizeId => $prizeCount) {
                $drawPrizes[$prizeId] = isset($drawPrizes[$prizeId]) ? $drawPrizes[$prizeId] + $prizeCount : $prizeCount;
            }
            if (empty($drawPrizes) || array_sum($drawPrizes) <= 0) {
                throw new BasicException(-1, '奖池为空');
            }

            // ================================================ 数据存储 ================================================
            $userCompensateCoin = 0;
            if ($compensateCoin && ($user->output - $user->input < 0) && (abs($user->output - $user->input) > $compensateCoin)) {
                if ($compensateCoin <= 8000) {
                    $userCompensateCoin = 130000;
                } elseif ($compensateCoin <= 130000) {
                    $userCompensateCoin = 170000;
                } elseif ($compensateCoin <= 170000) {
                    $userCompensateCoin = 300000;
                } elseif ($compensateCoin <= 300000) {
                    $userCompensateCoin = 500000;
                } else {
                    $userCompensateCoin = 300000;
                }
            }
            foreach ($drawPrizes as $prizeId => $prizeCount) {
                $gift       = $allPrizes->where('prize_id', $prizeId)->first();
                $price      = $gift->gift->price ?? 0;
                $drawOutput += $prizeCount * $price;
            }

            // 1. 用户资金变更
            $balanceResult = User::query()->where('id', $userId)
                ->where('balance', $user->balance)
                ->update([
                    "count"           => DB::raw("`count` + $count"),
                    "input"           => DB::raw("`input` + $drawInput"),
                    "output"          => DB::raw("`output` + $drawOutput"),
                    "balance"         => DB::raw(" `balance` - $count"),
                    "compensate_coin" => $userCompensateCoin,
                ]);
            if (!$balanceResult) {
                throw new BasicException(-1, '请求太快');
            }
            // 2. 背包增加礼物
            (new GiftBackpack())->addDraws($userId, $drawPrizes);
            // 3. 抽奖记录
            $draw = Draw::query()->create(['plate_id' => $plateId, 'user_id' => $userId, 'count' => $count, 'prize_count' => array_sum($drawPrizes), 'input' => $drawInput, 'output' => $drawOutput]);
            // 4. 抽奖详情
            $drawRecordData = [];
            $broadcastCoin  = $broadcastCount = $notifyCoin = $notifyCount = 0;
            $notifyPushData = $broadcastPushData = [];
            foreach ($drawPrizes as $prizeId => $prizeCount) {
                $gift             = $allPrizes->where('prize_id', $prizeId)->first();
                $prizePrice       = $gift->price ?? 0;
                $drawRecordData[] = ['user_id' => $userId, 'draw_id' => $draw->id, 'prize_id' => $prizeId, 'prize_price' => $prizePrice, 'count' => $prizeCount];

                if ($prizePrice >= $setting->notify_coin) {
                    $notifyGift        = Collection::make([]);
                    $notifyGift->name  = $gift->gift->name ?? '';
                    $notifyGift->price = $prizePrice;
                    $notifyGift->count = $prizeCount;

                    $notifyPushData[] = $notifyGift;
                    $notifyCoin       += $prizePrice;
                    $notifyCount      += 1;
                }

                if ($prizePrice >= $setting->broadcast_coin) {
                    $broadcastGift          = Collection::make([]);
                    $broadcastGift->name    = $gift->gift->name ?? '';
                    $broadcastGift->price   = $prizePrice;
                    $broadcastGift->count   = $prizeCount;
                    $broadcastGift->picture = $gift->gift->picture ?? '';

                    $broadcastPushData[] = $broadcastGift;
                    $broadcastCoin       += $prizePrice;
                    $broadcastCount      += 1;
                }
            }
            DrawRecord::query()->insert($drawRecordData);
            // 5. 奖池投入记录
            Pool::query()->where('type', $poolType)->update([
                "count"  => DB::raw("`count` + $count"),
                "input"  => DB::raw("`input` + $drawInput"),
                "output" => DB::raw("`output` + $drawOutput")
            ]);
            // 6. 每日统计
            UserStatDateRepository::getInstance()->inc($userId, [
                'count'           => $count,
                'input'           => $drawInput,
                'output'          => $drawOutput,
                'notify_count'    => $notifyCount,
                'notify_coin'     => $notifyCoin,
                'broadcast_count' => $broadcastCount,
                'broadcast_coin'  => $broadcastCoin,
            ]);
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            $message = $exception->getCode() == -1 ? $exception->getMessage() : '系统繁忙';
            throw new BasicException($exception->getCode(), $message);
        }


        $user = RedisService::getUserChatLabel($userId);
        foreach ($notifyPushData as $value) {
            (new PushChatService())->lottery($plate->name, $user, $value);
        }
        (new PushService())->lottery($plate->name, $broadcastPushData, $user);
        return $draw;
    }

    /**
     * @param $drawType 抽奖类型 gashapon:航海-神灯  pet:超级夺宝 senior:至尊夺宝 stellar:幸运神灯
     * @param $tag 池子类型 public:公共池, personage:独立奖池  novice:新手奖池 sup:修正池 single:单抽奖池(已废弃)
     * @return bool
     * @throws BasicException
     */
    public function clearPoolPrize($drawType, $tag)
    {
        $redis = app('redis')->connection('game');
        switch ($drawType) {
            case 'gashapon':
                $model_pool_config = app('model_gashapon_pool_config');
                $model_pool_prize = app('model_gashapon_pool_prize');
                $redisKey = 'gashapon:';
                break;
            case 'pet':
                $model_pool_config = app('model_lottery_pet_pool_config');
                $model_pool_prize = app('model_lottery_pet_pool_prize');
                $redisKey = 'pet:';
                break;
            case 'senior':
                $model_pool_config = app('model_senior_pool_config');
                $model_pool_prize = app('model_senior_pool_prize');
                $redisKey = 'senior:';
                break;
            case 'stellar':
                $model_pool_config = app('model_stellar_pool_config');
                $model_pool_prize = app('model_stellar_pool_prize');
                $redisKey = 'stellar:';
                break;
            case 'fantasy':
                $model_pool_config = app('model_fantasy_pool_config');
                $model_pool_prize = app('model_fantasy_pool_prize');
                $redisKey = 'fantasy:';
                break;
            case 'frag':
                $model_pool_config = app('model_frag_pool_config');
                $model_pool_prize = app('model_frag_pool_prize');
                $redisKey = 'frag:';
                break;
            default :
                throw new BasicException(-1, '类型错误');
                break;
        }
        $pool_config = $model_pool_config->where('id', 1)->first();
        if ($pool_config->switch) {
            throw new BasicException(-1, '请先关闭游戏');
        }
        $redisKey = $redisKey . "prizeList_" . $tag;
        $model_pool_prize->where('tag', $tag)->update(['count' => 0]);
        $redis->del($redisKey);
        return true;
    }


}
